/**
 * 
 */
package org.vsg.stock.core.algorithm;

import java.text.DecimalFormat;
import java.util.List;

import org.vsg.stock.core.indicator.kline.DegreesCoordinatePoint;

/**
 * @author Bill Vison
 *
 */
public class EstimatedLineRegression {
	
	
	private double coefficient;
	
	
	private List<DegreesCoordinatePoint> points;
	
	private double maxDegree = 0;
	
	private double minDegree = 90;
	
	public void setPoints(List<DegreesCoordinatePoint> points) {
		
		this.points = points;
		
		for (DegreesCoordinatePoint point : points) {
			double degrees = point.getDegrees();
			minDegree = Math.min(degrees, minDegree);
			if ( minDegree < 0 ) {
				System.out.println( point );
			}
			maxDegree = Math.max( degrees , maxDegree);

		}
		
	}
	
	/**
	 * calculate the object value
	 */
	public void calculate() {
		
		double step = 1;
		int factor = 10;
		double calMinDegree = this.minDegree * factor;
		double calMaxDegree = this.maxDegree * factor;

		double smallestCompareCoef = 999999;
		double smallestDistance = 99999999;
		double degreeOfSmallestDistance = 0;
		for (double i = calMinDegree ; i <= calMaxDegree ; i=i+step) {
			// --- calculate all point ---
			double compareCoef = getCoefficientA(i / factor);

			// --- get the all distance ---
			double avgDistance = calculateAvgDistance(compareCoef);
			
			// --- mark the least distance ---
			if ( avgDistance < smallestDistance) {
				smallestDistance = Math.min( avgDistance , smallestDistance);
				smallestCompareCoef = compareCoef;
				degreeOfSmallestDistance = i;
			}
			
		}
		
		smallestCompareCoef = findCompareCoefInArea(degreeOfSmallestDistance , smallestCompareCoef);
		
		coefficient = smallestCompareCoef;
		
	}

	

	/**
	 * get find the coefficient
	 * @param inputCoef
	 * @return
	 */
	private double findCompareCoefInArea(double degreeInArea , double inputCompareCoef) {
		double step = 1;
		int factor = 10;
		double calMinDegree = Math.floor(degreeInArea) * factor;
		double calMaxDegree = Math.ceil(degreeInArea) * factor;

		double smallestCompareCoef = 999999;
		double smallestDistance = 99999999;
		double degreeOfSmallestDistance = 0;
		for (double i = calMinDegree ; i <= calMaxDegree ; i=i+step) {
			// --- calculate all point ---
			double compareCoef = getCoefficientA(i / factor);
			// --- get the all distance ---
			double avgDistance = calculateAvgDistance(compareCoef);
			if ( avgDistance < smallestDistance) {
				smallestDistance = Math.min( avgDistance , smallestDistance);
				smallestCompareCoef = compareCoef;
				degreeOfSmallestDistance = i;
			}			
		}
		
		// --- find more smaller value  ----
		
		return smallestCompareCoef;
	}	
	
	
	private double calculateAvgDistance(double compareCoef) {
		double sumDistance = 0;
		for (DegreesCoordinatePoint point : points) {
			double distance = calPointToLineDistance(  compareCoef , point );
			sumDistance = sumDistance + Math.pow( distance , 2);
		}
		double avgDistance = Math.sqrt( sumDistance ) / points.size();
		return avgDistance;
	}

	
	private static DecimalFormat decFormat = new DecimalFormat("0.######");
	
	private double getCoefficientA(double degree) {
		double tagCoeff = Double.parseDouble( decFormat.format( Math.tan( Math.toRadians( degree ) ) ) );
		
		return tagCoeff;
	}
	
	private double toDegree(double value) {
		return Double.parseDouble( decFormat.format( Math.toDegrees( Math.atan( value ) ) ) );
	}
	
	
	private double calPointToLineDistance(double a , DegreesCoordinatePoint point) {
		
		double pointX = point.getXvalue();
		double pointY = point.getYvalue();
		
		/**
		 * point to line formula
		 * ax+by+c = 0
		 * 
		 */
		double b = -1;
		double distance = Math.abs( a * pointX + b * pointY ) / Math.sqrt( Math.pow(a, 2) + Math.pow(b, 2) );
		
		return distance;
	}
	
	
	public double getCoefficient() {
		return this.coefficient;
	}
	
	
	
}
